﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Netcached
{
    class SocketPool : IDisposable
    {
        public int MaxSockets { get; set; }

        object socketLock;
        object socketDeletionLock;
        object socketReturnLock;
        Queue<SocketWrapper> socketQueue;
        List<SocketWrapper> socketList;
        string serverAddress;
        int port;
        int sendReceiveTimeout;
        bool disposed;

        public int UsedBy { get; set; }

        public SocketPool(string serverAddress, int port, int sendReceiveTimeout, int maxSockets)
        {
            this.serverAddress = serverAddress;
            this.port = port;
            this.sendReceiveTimeout = sendReceiveTimeout;
            this.MaxSockets = maxSockets;

            socketLock = new object();
            socketQueue = new Queue<SocketWrapper>();
            socketList = new List<SocketWrapper>();
        }

        public SocketWrapper GetSocket()
        {
            SocketWrapper socket = null;

            lock (socketLock)
            {
                while (socketQueue.Count > 0 && socket == null)
                {
                    socket = socketQueue.Dequeue();
                    if (socket != null)
                    {
                        if (!socket.IsAlive)
                        {
                            RemoveSocketFromPool(socket);
                            socket = null;
                        }
                        else if (socket.IsWorking)
                        {
                            throw new InvalidOperationException("Can not dequeue a socket which is currently in use.");
                        }
                    }
                }

                if (socket != null)
                {
                    socket.IsWorking = true;
                }
            }

            if (socket == null)
            {
                socket = CreateSocket();
                System.Diagnostics.Debug.Write(string.Format("Sock num: {0}. Creating socket. id: {1}", socketList.Count, socket.Identifier) + "\r\n");
            }

            System.Diagnostics.Debug.Write(string.Format("Sock num: {0}. Getting socket. id: {1}... {2}", socketList.Count, socket.Identifier, DateTime.Now.Millisecond) + "\r\n");
            
            return socket;
        }

        private SocketWrapper CreateSocket()
        {
            SocketWrapper socket = new SocketWrapper(serverAddress, port, sendReceiveTimeout, this);
            socketList.Add(socket);
            return socket;
        }

        public void ReturnSocketToPool(SocketWrapper socket)
        {
            if (socket != null)
            {
                if (socketQueue.Count < MaxSockets && socket.IsAlive)
                {
                    socketQueue.Enqueue(socket);
                    System.Diagnostics.Debug.Write(string.Format("Sock num: {0}. Enqueing socket. id: {1}", socketList.Count, socket.Identifier) + "\r\n");
                }
                else
                {
                    System.Diagnostics.Debug.Write(string.Format("Sock num: {0}. Destroying socket. id: {1}", socketList.Count, socket.Identifier) + "\r\n");
                    RemoveSocketFromPool(socket);
                }
            }
        }

        public void RemoveSocketFromPool(SocketWrapper socket)
        {
            socketList.Remove(socket);
            socket.Dispose();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    while (socketList.Count > 0)
                    {
                        var socket = socketList[0];
                        if (socket != null)
                        { 
                            lock (socketLock)
                            {
                                socket.Dispose();
                            }
                        }

                        socketList.RemoveAt(0);
                    }
                }

                disposed = true;
            }
        }
    }
}
